package genetica.algoritmoSeleccion;

import genetica.cromosomas.Cromosoma;
import genetica.cromosomas.factorias.FactoriaCromosomas;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Random;
import java.util.TreeMap;
import java.util.TreeSet;

public class ASRanking extends AlgoritmoSeleccion {
	private static double beta;

	public void setBeta(double beta) {
		ASRanking.beta = beta;
	}

	public double getBeta() {
		return ASRanking.beta;
	}

	public ASRanking(double beta) {
		super();
		if (beta > 2 || beta < 1)
			throw new Error("EL beta tiene que estar entre 1 y 2");
		ASRanking.beta = beta;
	}

	@Override
	public String toString() {
		return "Algoritmo de selección por Rankin";
	}

	public ArrayList<Cromosoma> seleccion__(ArrayList<Cromosoma> pob,
			FactoriaCromosomas<Cromosoma> fc) {
		Random r = new Random();
		TreeSet<Cromosoma> cromosmasOrdenados = new TreeSet<Cromosoma>();
		int max = 0;
		for (Cromosoma c : pob) {
			cromosmasOrdenados.add(c);

			max++;
		}
		// De menos a mas
		Cromosoma[] cromosomas = (Cromosoma[]) cromosmasOrdenados.toArray();
		ArrayList<Cromosoma> nuevaPob = new ArrayList<Cromosoma>();
		boolean hayCromosoma = false;
		for (int j = 0; j < cromosomas.length; j++) {
			// Muy poco probable que de mas de una vuelta en concreto 1/n²
			// Cromosomas, aun asi mantienen el esquema de probabilidad
			do {
				for (int i = cromosomas.length - 1; !hayCromosoma || i >= 0; i--) {
					hayCromosoma = r.nextBoolean();
					if (hayCromosoma) {
						nuevaPob.add(fc.copia(cromosomas[i]));
					}
				}
			} while (!hayCromosoma);
		}

		return nuevaPob;
	}

	private double[] rankPopulation(int size) {
		double[] fitnessSegments = new double[size];
		for (int i = 0; i < fitnessSegments.length; i++) {
			double probOfIth = (double) i / size;
			probOfIth = probOfIth * 2 * (beta - 1);
			probOfIth = beta - probOfIth;
			probOfIth = (double) probOfIth * ((double) 1 / size);
			if (i != 0)
				fitnessSegments[i] = fitnessSegments[i - 1] + probOfIth;
			else
				fitnessSegments[i] = probOfIth;
		}
		return fitnessSegments;
	}

	@Override
	public ArrayList<Cromosoma> seleccion(ArrayList<Cromosoma> pob,FactoriaCromosomas<Cromosoma> fc) {
//		TreeMap<Double, Cromosoma> cromosmasOrdenados = new TreeMap<Double, Cromosoma>();
		TreeSet<Cromosoma> cromosmasOrdenados = new TreeSet<Cromosoma>(Cromosoma.getComparadorSinIgualdad());
		double aptitudAnterior =  0;
		Cromosoma anterior = null;
		for (Cromosoma c : pob) {
			double estaAptitud = c.getAptitud();
			Cromosoma actual = c;
			cromosmasOrdenados.add(c);
			int size = cromosmasOrdenados.size();
			actual = c;
			aptitudAnterior = c.getAptitud();
			anterior = c;
		}
		// Individual[] sortedPop = pob.selectionSort();
		Cromosoma[] sortedPop = new Cromosoma[pob.size()];
//		Cromosoma[] sortedPop = cromosmasOrdenados.
		for (int i = 0; i < pob.size();i++){
			Cromosoma c = cromosmasOrdenados.pollLast();
			sortedPop[i] = c;
		}
		ArrayList<Cromosoma> nuevaPob = new ArrayList<Cromosoma>();
		nuevaPob.add(fc.copia(sortedPop[0]));
		nuevaPob.add(fc.copia(sortedPop[1]));
		double[] fitnessSegments = rankPopulation(pob.size());
		double entireSegment = fitnessSegments[fitnessSegments.length - 1];
		while (pob.size() > nuevaPob.size()) {
			double x = (double) (Math.random() * entireSegment);
			if (x <= fitnessSegments[0]) {
				/*** First Idividual was Selected **/
				// futureParents[numOfParents]=sortedPop[0];
				nuevaPob.add(fc.copia(sortedPop[0]));
			} else
				for (int i = 1; i < pob.size(); i++)
					if (x > fitnessSegments[i - 1] && x <= fitnessSegments[i]) {
						/*** i'th Idividual was Selected **/
						nuevaPob.add(fc.copia(sortedPop[i]));
					}
		}
		return nuevaPob;
	}
	
	
	
}
